package com.uinnova.product.eam.service.impl;

import com.binary.core.exception.BinaryException;
import com.binary.core.util.BinaryUtils;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.comm.model.es.AutoLayoutDiagramConf;
import com.uinnova.product.eam.model.AutoDiagramCiVo;
import com.uinnova.product.eam.model.EamAutoLayoutCdt;
import com.uinnova.product.eam.model.diagram.DiagramNodeLinkInfo;
import com.uinnova.product.eam.model.vo.CiSimpleInfoVo;
import com.uinnova.product.eam.service.AutoLayoutDiagramSvc;
import com.uinnova.product.eam.service.ICIRltSwitchSvc;
import com.uinnova.product.eam.service.ICISwitchSvc;
import com.uinnova.product.eam.service.IEamCIClassApiSvc;
import com.uinnova.product.eam.service.es.AutoLayoutDiagramConfDao;
import com.uinnova.product.eam.service.utils.VisualModelUtils;
import com.uinnova.product.vmdb.comm.model.ci.CcCi;
import com.uinnova.product.vmdb.comm.model.ci.CcCiClass;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uinnova.product.vmdb.provider.rlt.bean.CcCiRltInfo;
import com.uino.bean.cmdb.base.*;
import com.uino.bean.permission.base.SysUser;
import com.uino.dao.cmdb.ESVisualModelSvc;
import com.uino.util.sys.SysUtil;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 自由绘图自动成图配置
 */
@Service
public class AutoLayoutDiagramSvcImpl implements AutoLayoutDiagramSvc {
    @Resource
    AutoLayoutDiagramConfDao layoutDiagramConfDao;
    @Resource
    private ESVisualModelSvc esVisualModelSvc;
    @Resource
    private IEamCIClassApiSvc ciClassApiSvc;
    @Resource
    private ICISwitchSvc ciSwitchSvc;
    @Resource
    private ICIRltSwitchSvc rltSwitchSvc;

    @Override
    public Long savaOrUpdate(AutoLayoutDiagramConf layoutDiagramConf) {
        checkNotNull(layoutDiagramConf);
        AutoLayoutDiagramConf autoLayoutDiagramConf = getConfByDiagramIdAndSheetId(layoutDiagramConf.getDiagramId(), layoutDiagramConf.getSheetId());
        long createTime = System.currentTimeMillis();
        String loginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        if (!BinaryUtils.isEmpty(autoLayoutDiagramConf)) {
            layoutDiagramConf.setId(autoLayoutDiagramConf.getId());
            layoutDiagramConfDao.deleteById(autoLayoutDiagramConf.getId());
        }
        if (BinaryUtils.isEmpty(layoutDiagramConf.getId())) {
            layoutDiagramConf.setCreateTime(createTime);
            layoutDiagramConf.setCreator(loginCode);
        }
        layoutDiagramConf.setModifier(loginCode);
        layoutDiagramConf.setModifyTime(createTime);
        layoutDiagramConf.setDomainId(SysUtil.getCurrentUserInfo().getDomainId());
        return layoutDiagramConfDao.saveOrUpdate(layoutDiagramConf);
    }

    private void checkNotNull(AutoLayoutDiagramConf layoutDiagramConf) {
        if (BinaryUtils.isEmpty(layoutDiagramConf.getDiagramId())) {
            throw new BinaryException("视图ID不能为空");
        }
        if (BinaryUtils.isEmpty(layoutDiagramConf.getSheetId())) {
            throw new BinaryException("页面ID不能为空");
        }
    }

    @Override
    public AutoLayoutDiagramConf getConfByDiagramIdAndSheetId(String diagramId, String sheetId) {
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termsQuery("diagramId.keyword", diagramId));
        query.must(QueryBuilders.termsQuery("sheetId.keyword", sheetId));
        return layoutDiagramConfDao.selectOne(query);
    }

    @Override
    public List<CiSimpleInfoVo> getBusinessElement(Long classId) {
        //获取当前使用的元模型信息
        ESVisualModel esVisualModel = esVisualModelSvc.getEnableModel(SysUtil.getCurrentUserInfo().getDomainId());
        List<CiSimpleInfoVo> result = new ArrayList<>();
        Set<Long> filterSet;
        if(BinaryUtils.isEmpty(classId)){
            //查全部
            filterSet = VisualModelUtils.getCiClassIds(esVisualModel);
        }else{
            //查下级
            filterSet = VisualModelUtils.getLowerClassIds(esVisualModel, classId);
        }
        if(CollectionUtils.isEmpty(filterSet)){
            return result;
        }
        List<ESCIClassInfo> classList = ciClassApiSvc.selectCiClassByIds(new ArrayList<>(filterSet));
        for (ESCIClassInfo each : classList) {
            CiSimpleInfoVo classInfo = new CiSimpleInfoVo(each.getId(), each.getClassName());
            result.add(classInfo);
        }
        return result;
    }

    @Override
    public List<AutoDiagramCiVo> getBusinessData(List<EamAutoLayoutCdt> cdt) {
        SysUser user = SysUtil.getCurrentUserInfo();
        //先查第一层的
        Set<Long> classIds = new HashSet<>();
        Map<Long, List<EamAutoLayoutCdt>> configGroup = new HashMap<>();
        for (EamAutoLayoutCdt each : cdt) {
            classIds.add(each.getClassId());
            if(!CollectionUtils.isEmpty(each.getChildren())){
                configGroup.computeIfAbsent(each.getClassId(), key -> new ArrayList<>()).addAll(each.getChildren());
            }
        }
        List<ESCIClassInfo> classList = ciClassApiSvc.selectCiClassByIds(new ArrayList<>(classIds));
        Map<Long, ESCIClassInfo> classMap = classList.stream().collect(Collectors.toMap(CcCiClass::getId, e -> e, (k1, k2) -> k2));
        List<ESCIInfo> ciList = ciSwitchSvc.getCiByClassIds(new ArrayList<>(classIds), user.getLoginCode(), LibType.DESIGN);
        List<AutoDiagramCiVo> result = new ArrayList<>();
        Set<String> distinct = new HashSet<>();
        for (ESCIInfo each : ciList) {
            ESCIClassInfo classInfo = classMap.get(each.getClassId());
            CcCiInfo ciInfo = EamUtil.coverESCIInfo(each, classInfo.getCcAttrDefs(), classInfo);
            AutoDiagramCiVo copy = EamUtil.copy(ciInfo, AutoDiagramCiVo.class);
            distinct.add(each.getCiCode());
            result.add(copy);
        }
        ESVisualModel esVisualModel = esVisualModelSvc.getEnableModel(user.getDomainId());
        List<DiagramNodeLinkInfo> rltList = VisualModelUtils.getRltClassIds(esVisualModel);
        Map<Long, List<DiagramNodeLinkInfo>> vmGroup = rltList.stream().collect(Collectors.groupingBy(DiagramNodeLinkInfo::getSourceId));
        this.getDataByConfig(result, new ArrayList<>(result), configGroup, vmGroup, distinct, user.getLoginCode());
        //转化为私有库对象返回
        this.coverToPrivate(result, user.getLoginCode());
        return result;
    }

    /**
     * 将设计库对象copy进入私有库
     * @param ciList ci集合
     * @param ownerCode 用户标识
     */
    private void coverToPrivate(List<AutoDiagramCiVo> ciList, String ownerCode) {
        if(CollectionUtils.isEmpty(ciList)){
            return;
        }
        List<ESCIInfo> coverList = new ArrayList<>();
        Set<Long> classIds = new HashSet<>();
        for (AutoDiagramCiVo each : ciList) {
            ESCIInfo copy = EamUtil.coverCiInfo(each);
            copy.setId(null);
            copy.setOwnerCode(ownerCode);
            coverList.add(copy);
            classIds.add(each.getCi().getClassId());
        }
        Map<String, ? extends SaveBatchCIContext> contextMap = ciSwitchSvc.saveOrUpdateBatchCI(coverList, new ArrayList<>(classIds), ownerCode, ownerCode, LibType.PRIVATE);
        Map<String, ESCIInfo> ciMap = contextMap.values().stream().map(SaveBatchCIContext::getEsCi).collect(Collectors.toMap(CcCi::getCiCode, e -> e, (k1, k2) -> k2));
        //刷一下结果集ciId及ownerCode
        for (AutoDiagramCiVo each : ciList) {
            ESCIInfo newCi = ciMap.get(each.getCi().getCiCode());
            if(newCi == null){
                continue;
            }
            each.getCi().setId(newCi.getId());
        }
    }

    private void getDataByConfig(List<AutoDiagramCiVo> result, List<AutoDiagramCiVo> parentList, Map<Long, List<EamAutoLayoutCdt>> configGroup,
                                 Map<Long, List<DiagramNodeLinkInfo>> vmGroup, Set<String> distinct, String ownerCode){
        Set<Long> sourceIds = new HashSet<>();
        Set<Long> rltIds = new HashSet<>();
        Set<Long> targetIds = new HashSet<>();
        Set<String> rltRange = new HashSet<>();
        Map<Long, List<EamAutoLayoutCdt>> childConfigGroup = new HashMap<>();
        for (Map.Entry<Long, List<EamAutoLayoutCdt>> entry : configGroup.entrySet()) {
            List<DiagramNodeLinkInfo> vmLinkList = vmGroup.get(entry.getKey());
            if(CollectionUtils.isEmpty(vmLinkList)){
                continue;
            }
            Map<Long, DiagramNodeLinkInfo> vmTargetMap = vmLinkList.stream().collect(Collectors.toMap(DiagramNodeLinkInfo::getTargetId, e -> e, (k1, k2) -> k2));
            for (EamAutoLayoutCdt each : entry.getValue()) {
                DiagramNodeLinkInfo linkInfo = vmTargetMap.get(each.getClassId());
                if(linkInfo == null){
                    continue;
                }
                sourceIds.add(linkInfo.getSourceId());
                rltIds.add(linkInfo.getLinkId());
                targetIds.add(linkInfo.getTargetId());
                rltRange.add(linkInfo.getLinkKey());
                if(!CollectionUtils.isEmpty(each.getChildren())){
                    childConfigGroup.computeIfAbsent(each.getClassId(), key -> new ArrayList<>()).addAll(each.getChildren());
                }
            }
        }
        if(CollectionUtils.isEmpty(rltIds)) {
            return;
        }
        List<CcCiRltInfo> data = rltSwitchSvc.queryRltByClassIds(rltIds, sourceIds, targetIds);
        if(CollectionUtils.isEmpty(data)){
            return;
        }
        List<AutoDiagramCiVo> childList = new ArrayList<>();
        Map<String, List<CcCiRltInfo>> rltGroup = data.stream().collect(Collectors.groupingBy(e -> e.getSourceCiInfo().getCi().getCiCode()));
        for (AutoDiagramCiVo each : parentList) {
            String ciCode = each.getCi().getCiCode();
            List<CcCiRltInfo> rltInfoList = rltGroup.get(ciCode);
            if(CollectionUtils.isEmpty(rltInfoList)){
                continue;
            }
            for (CcCiRltInfo rlt : rltInfoList) {
                CcCiInfo targetCiInfo = rlt.getTargetCiInfo();
                String key = each.getCi().getClassId() + "_" + rlt.getCiRlt().getClassId() + "_" + targetCiInfo.getCi().getClassId();
                if(!rltRange.contains(key) || distinct.contains(targetCiInfo.getCi().getCiCode())){
                    continue;
                }
                AutoDiagramCiVo copy = EamUtil.copy(targetCiInfo, AutoDiagramCiVo.class);
                copy.setParentCode(ciCode);
                copy.getCi().setOwnerCode(ownerCode);
                distinct.add(copy.getCi().getCiCode());
                childList.add(copy);
            }
        }
        if(CollectionUtils.isEmpty(childList)){
            return;
        }
        result.addAll(childList);
        this.getDataByConfig(result, childList, childConfigGroup, vmGroup, distinct, ownerCode);
    }
}
